package com.bs.sc.base.utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;

import com.bs.sc.mgr.goods.dao.GoodsDao;
import com.bs.sc.mgr.goods.entity.Goods;

public class BasedUserRecommendation {

	public static List<Map<String, String>> userRecommendation(String userId, GoodsDao goodsDao) {
		// 获取【用户-商品-评分】数据矩阵
		List<String> userGoodsGradelist = FileUtil.readFile(PropertiesUtil.getPropertiesValue("cf.file"));
		Map<String, Map<String, String>> userGoodsGradeTrain = DataUtil.getRecommendData(userGoodsGradelist);
		// 获取【用户-商品-评分】相似度矩阵
		Map<String, Map<String, Double>> w = BasedUserRecommendation.simpleUserSimilarity(userGoodsGradeTrain);
		// 获取【用户-分类-评分】数据矩阵
		List<String> userclassifyGradelist = FileUtil.readFile(PropertiesUtil.getPropertiesValue("cf.classfiy.file"));
		Map<String, Map<String, String>> userClassifyGradeTrain = DataUtil.getRecommendData(userclassifyGradelist);
		// 获取【用户-分类-评分】相似度矩阵
		Map<String, Map<String, Double>> wClassify = BasedUserRecommendation
				.simpleUserSimilarity(userClassifyGradeTrain);
		// 取最相似的20个用户，取推荐评分最高的6个商品
		List<Map<String, String>> goodsList = recommend(userId, userGoodsGradeTrain, userClassifyGradeTrain, w,
				wClassify, 20, 6, goodsDao);
		return goodsList;
	}

	/**
	 * 推荐
	 * 
	 * @param userId 当前用户
	 * @param train  数据矩阵
	 * @param w      相似度矩阵
	 * @param ucount 取相似用户个数
	 * @param gcount 推送商品个数
	 * @return
	 */
	public static List<Map<String, String>> recommend(String userId, Map<String, Map<String, String>> train,
			Map<String, Map<String, String>> classfiyTrain, Map<String, Map<String, Double>> w,
			Map<String, Map<String, Double>> wClassify, int ucount, int gcount, GoodsDao goodsDao) {
		Map<String, Double> temp = new HashMap<String, Double>();
		// 最终推送结果
		List<Map<String, String>> result = new ArrayList<>();
		if (w.isEmpty() || !w.containsKey(userId)) {
			return result;
		}
		if (wClassify.isEmpty() || !wClassify.containsKey(userId)) {
			return result;
		}
		// 当前用户的购物列表(物品：评分)
		Map<String, String> goodsMap = train.get(userId);
		// 获取当前用户的相似度矩阵【用户-商品-评分】
		Map<String, Double> userSimilarity = w.get(userId);
		// 对当前用户的相似度矩阵进行排序（用户：相似度）
		Map<String, Double> userSimilaritySort = DataUtil.sortDescend(userSimilarity);

		// 获得当前用户的相似度矩阵【用户-分类-评分】
		Map<String, Double> userSimilarityClassify = wClassify.get(userId);
		// 对当前用户的兴趣相似度矩阵进行排序（用户：相似度）
		Map<String, Double> userSimilaritySortClassify = DataUtil.sortDescend(userSimilarityClassify);
		int i = 0;
		// 遍历当前用户相似度排序后的矩阵
		for (Entry<String, Double> ue : userSimilaritySort.entrySet()) {
			for (Entry<String, Double> uec : userSimilaritySortClassify.entrySet()) {
				Map<String, String> classifyMap = classfiyTrain.get(uec.getKey());
				// 获取相似用户的购物列表(物品：评分)
				Map<String, String> ueGoodsMap = train.get(ue.getKey());
				// 遍历相似用户购物列表，排除当前用户已经购买的商品
				for (Entry<String, String> ueGoods : ueGoodsMap.entrySet()) {
					if (goodsMap.containsKey(ueGoods.getKey())) {
						continue;
					}
					double grade = 0;
					Optional<Goods> optional = goodsDao.findById(ueGoods.getKey());
					if (optional.isPresent()) {
						Goods goods = optional.get();
						String classfiy = String.valueOf(goods.getClassify());
						for (Entry<String, String> ud : classifyMap.entrySet()) {
							String c = ud.getKey();
							if (classfiy.equals(c)) {
								grade = (ue.getValue() * Double.valueOf(ueGoods.getValue())) * 0.6
										+ (uec.getValue() * Double.valueOf(ud.getValue())) * 0.4;
							}
						}
					}
					temp.put(ueGoods.getKey(), grade);
				}
			}
			// 如果相似度用户达到了预期值，可以终结推算，把计算出的结果推算给当前用户
			if (i >= ucount) {
				break;
			}
			i++;
		}
		temp = DataUtil.sortDescend(temp);
		int j = 0;
		for (Entry<String, Double> e : temp.entrySet()) {
			if (j >= gcount) {
				break;
			}
			Map<String, String> temp_ = new HashMap<>();
			temp_.put("goodsId", String.valueOf(e.getKey()));
			double g = e.getValue();
			int g_ = (int) Math.ceil(g);
			if (g_ > 5)
				g_ = 5;
			temp_.put("grade", String.valueOf(g_));
			result.add(temp_);
			j++;
		}
		return result;
	}

	/**
	 * 优化了上面的相似度计算，去除了没有交集的数据
	 * 
	 * @param map
	 * @return
	 */
	public static Map<String, Map<String, Double>> simpleUserSimilarity(Map<String, Map<String, String>> map) {
		Map<String, List<String>> invertedMap = invertedMap(map);
		Map<String, Map<String, Double>> C = getItemCount(invertedMap);
		Map<String, Integer> N = getUserCount(invertedMap);
		Map<String, Map<String, Double>> result = new HashMap<String, Map<String, Double>>();
		for (Entry<String, Map<String, Double>> relatedUsers : C.entrySet()) {
			Map<String, Double> temp = new HashMap<String, Double>();
			Map<String, Double> cuvMap = relatedUsers.getValue();
			for (Entry<String, Double> cuv : cuvMap.entrySet()) {
				double c = cuv.getValue();
				int utotal = N.get(relatedUsers.getKey());
				int vtotal = N.get(cuv.getKey());
				double similarity = c / Math.sqrt(utotal * vtotal);
				temp.put(cuv.getKey(), similarity);
			}
			result.put(relatedUsers.getKey(), temp);
		}
		return result;
	}

	/**
	 * 存放用户对应的item数量
	 * 
	 * @param invertedMap
	 * @return
	 */
	private static Map<String, Integer> getUserCount(Map<String, List<String>> invertedMap) {
		Map<String, Integer> map = new HashMap<String, Integer>();
		for (String item : invertedMap.keySet()) {
			List<String> users = invertedMap.get(item);
			for (String user : users) {
				if (map.containsKey(user)) {
					int count = map.get(user);
					map.put(user, count + 1);
				} else {
					map.put(user, 1);
				}
			}
		}

		return map;
	}

	/**
	 * 统计用户与用户共同item数量集合 热门衰减
	 * 
	 * @param invertedMap
	 * @return
	 */
	private static Map<String, Map<String, Double>> getItemCount(Map<String, List<String>> invertedMap) {
		Map<String, Map<String, Double>> result = new HashMap<String, Map<String, Double>>();
		for (String item : invertedMap.keySet()) {
			List<String> users = invertedMap.get(item);
			for (String user1 : users) {
				Map<String, Double> temp = new HashMap<String, Double>();
				for (String user2 : users) {
					if (user1.equals(user2)) {
						continue;
					}
					temp.put(user2, 1 / Math.log(1 + users.size()));
				}
				if (result.containsKey(user1)) {
					Map<String, Double> m = result.get(user1);
					for (Entry<String, Double> entry : m.entrySet()) {
						String mkey = entry.getKey();
						double mvalue = entry.getValue();
						for (Entry<String, Double> e : temp.entrySet()) {
							String tkey = e.getKey();
							if (mkey.equals(tkey)) {
								entry.setValue(mvalue + (1 / Math.log(1 + users.size())));
							}
						}
					}
				} else {
					result.put(user1, temp);
				}
			}
		}
		return result;
	}

	/**
	 * item ---> user 倒排表
	 * 
	 * @param map
	 */
	private static Map<String, List<String>> invertedMap(Map<String, Map<String, String>> map) {
		Map<String, List<String>> index = new HashMap<>();
		Set<String> keySet = map.keySet();
		for (String u : keySet) {
			Map<String, String> items = map.get(u);
			for (String i : items.keySet()) {
				if (!index.containsKey(i)) {
					List<String> users = new ArrayList<>();
					users.add(u);
					index.put(i, users);
				} else {
					List<String> users = index.get(i);
					users.add(u);
				}
			}
		}
		return index;
	}
}
